package com.dahuyou.domain.service;

import com.dahuyou.application.util.CharacterUtil;
import com.dahuyou.domain.model.vo.GenerateContext;
import com.dahuyou.domain.model.vo.SourceObjInfo;
import com.dahuyou.domain.model.vo.TargetObjInfo;
import com.dahuyou.application.util.Utils;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class Map2ObjGeneratorImpl extends AbstractMap2ObjGenerator {
    private int repair = 0;

    @Override
    protected void weavingSetCode(TargetObjInfo targetObjInfo,
                                  SourceObjInfo sourceObjInfo,
                                  GenerateContext generateContext) {
        Application application = ApplicationManager.getApplication();
        // 获取空格位置长度
        int distance = Utils.getWordStartOffset(generateContext.getEditorText(), generateContext.getOffset()) - generateContext.getStartOffset() - repair;
        application.runWriteAction(() -> {
            StringBuilder blankSpace = new StringBuilder();
            for (int i = 0; i < distance; i++) {
                blankSpace.append(" ");
            }
            int lineNumberCurrent = generateContext.getDocument().getLineNumber(generateContext.getOffset()) + 1;
            List<String> propsList = targetObjInfo.getPropsList();
            String targetVarName = targetObjInfo.getVariableName();
            String sourceVariableName = sourceObjInfo.getVariableName();
            for (String prop : propsList) {
                int lineStartOffset = generateContext.getDocument().getLineStartOffset(lineNumberCurrent++);
                WriteCommandAction.runWriteCommandAction(generateContext.getProject(), () -> {
                    // aa.setXXX(map.getKey("xxx"))
                    String weaveSetCode = blankSpace + targetVarName + ".set" + CharacterUtil.capitalizeFirstLetter(prop) + "(" + sourceVariableName + ".get(\"" + prop + "\"));\n";
                    generateContext.getDocument().insertString(lineStartOffset, weaveSetCode);
                });
            }
        });
    }

    @Override
    protected SourceObjInfo generateSourceObjInfo(GenerateContext generateContext) {
        // 获取用户复制的内容，如Map map
        String systemClipboardText = Utils.getSystemClipboardText().trim();
        String[] split = systemClipboardText.split("\\s");
        /*
        0 = "Map<String,"
        1 = "String>"
        2 = "sourceMap"
         */
        String variableName = split[split.length - 1].trim();
        SourceObjInfo sourceObjInfo = new SourceObjInfo();
        sourceObjInfo.setVariableName(variableName);
        return sourceObjInfo;
    }

    @Override
    protected TargetObjInfo generateTargetObjInfo(GenerateContext generateContext) {
        PsiClass psiClass = null;
        String variableName = null;
        PsiElement psiElement = generateContext.getPsiElement();
        // 如果是光标定位到类，即放在MyObj myobj中的MyObj
        if (psiElement instanceof PsiClass) {
            psiClass = (PsiClass) generateContext.getPsiElement();
            // 通过光标步长递进找到属性名称，往→移动，直到获取到右侧的变量名称
            PsiFile psiFile = generateContext.getPsiFile();
            Editor editor = generateContext.getEditor();
            int offsetStep = generateContext.getOffset() + 1;
            PsiElement elementAt = psiFile.findElementAt(editor.getCaretModel().getOffset());
            while (null == elementAt || elementAt.getText().equals(psiClass.getName()) || elementAt instanceof PsiWhiteSpace) {
                elementAt = psiFile.findElementAt(++offsetStep);
            }
            // 最终拿到属性名称，MyObj myobj = new MyObj()中的myobj
            variableName = elementAt.getText();
        }
        // 如果是光标定位到到属性，即放在MyObj myobj中的myobj
        if (psiElement instanceof PsiLocalVariable) {
            PsiLocalVariable psiLocalVariable = (PsiLocalVariable) psiElement;
            variableName = psiLocalVariable.getName();
            // 通过光标步长递进找到属性名称
            PsiFile psiFile = generateContext.getPsiFile();
            Editor editor = generateContext.getEditor();
            int offsetStep = generateContext.getOffset() - 1;
            PsiElement elementAt = psiFile.findElementAt(editor.getCaretModel().getOffset());
            while (null == elementAt
                    || elementAt.getText().equals(variableName)
                    || elementAt instanceof PsiWhiteSpace) {
                elementAt = psiFile.findElementAt(--offsetStep);
            }
            String clazzName = elementAt.getText();
            PsiClass[] psiClasses = PsiShortNamesCache.getInstance(generateContext.getProject()).getClassesByName(clazzName, GlobalSearchScope.projectScope(generateContext.getProject()));
            psiClass = psiClasses[0];
        }
        Pattern setMtd = Pattern.compile(setRegex);
        List<PsiClass> psiClassLinkList = getPsiClassLinkList(psiClass);
        List<String> propsList = new ArrayList<>();
        for (PsiClass psi : psiClassLinkList) {
            List<String> methodsList = getMethods(psi, setRegex, "set");
            for (String methodName : methodsList) {
                String prop = setMtd.matcher(methodName).replaceAll("$1").toLowerCase();
                propsList.add(prop);
            }
        }
        TargetObjInfo targetObjInfo = new TargetObjInfo();
        targetObjInfo.setPropsList(propsList);
        targetObjInfo.setVariableName(variableName);
        return targetObjInfo;
    }

    @Override
    protected GenerateContext generateUserContext(Project project, DataContext dataContext, PsiFile psiFile) {
        // 基础信息
        Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
        PsiElement psiElement = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
        assert editor != null;
        Document document = editor.getDocument();
        // 封装生成对象上下文
        GenerateContext generateContext = new GenerateContext();
        generateContext.setProject(project);
        generateContext.setPsiFile(psiFile);
        generateContext.setDataContext(dataContext);
        generateContext.setEditor(editor);
        generateContext.setPsiElement(psiElement);
        generateContext.setOffset(editor.getCaretModel().getOffset());
        generateContext.setDocument(document);
        generateContext.setLineNumber(document.getLineNumber(generateContext.getOffset()));
        generateContext.setStartOffset(document.getLineStartOffset(generateContext.getLineNumber()));
        generateContext.setEditorText(document.getCharsSequence());
        return generateContext;
    }
}
